I.傳值與傳參考 --> now
II. 傳參考與closure (1)
II. 傳參考與closure (2)
II. 傳參考與closure (3)
我記得以前學 C++ 的時候,對傳值、傳參考跟傳址有很多的討論,但在 js 好像比較少?最近比較常用 PHP ,以 PHP 為例,來做一個老問題:請實做一個函式,輸入兩個簡單型別的變數,例如兩個數字,然後將這兩個變數互相交換。
<?php
$a = 1;
$b = 2;
echo "($a, $b)"; // (1, 2)
swap($a, $b);
echo "($a, $b)"; // (2, 1)
function swap(&$a, &$b)
{
$temp = $a;
$a = $b;
$b = $temp;
}
這邊會看到 swap 中參數的部分加了一個 &,這是告訴 PHP 說這邊要傳遞的是變數的參考,但在 js 中無法做到這件事情,這也不是說 js 中沒有「值」跟「參考」的區別,只是在函式的參數傳遞時,無法明確指定這樣做,我想,這可能是 js 中比較少討論這塊的原因。
在 js 中,一般討論到值與參考時,大概就是在介紹變數的時候,我們都知道 js 中型別分為兩種,一種是簡單型別,像是數值、字串、布林值、null 與 undefined。其他的所有的值都是物件,通常物件在傳遞時,都是傳遞參考。
到底傳遞值跟參考有什麼差別?
值的傳遞是指在傳遞的時候,在記憶體中會要一塊新空間,然後將原本的值複製到其中,原本變數的修改或新變數的修改,都不會互相影響,例如:
var a = 1;
b = a;
console.log(a, b); // 1, 1
b = 2;
console.log(a, b); // 1, 2 a 還是 1
function swap(first, second){
var temp = first;
first = second;
second = temp;
console.log(first, second); // 2, 1 交換成功
}
var a = 1, b = 2;
swap(a, b);
console.log(a, b); // 1, 2 沒有被交換
物件的行為就不是這樣了,物件是傳遞參考,參考有點抽象,就先把它跟位址混在一起討論。可以想像在記憶體中,要兩塊空間,一塊空間是真的存資料,另外一塊空間,則是存那塊存資料的空間的位址。當要傳遞的時候,是傳遞這個位址;當要取資料的時候,就先去看一下位址是多少,在循着位址去找到真正的資料的儲存地方。也正因為存取時,是真的找到該資料的儲存地,所以修改的話會互相影響。
function swap(p){
var temp = p.a;
p.a = p.b;
p.b = temp;
}
var obj = {
a: 1,
b: 2
};
console.log(obj); // { a: 1, b: 2 }
swap(obj);
console.log(obj); // { a: 2, b: 1 }
在這個範例中,可以清楚地看到 a 跟 b 被交換了。
在記憶體中到底是怎麼一回事呢? 記憶體中會分兩種區塊:stack 跟 heap。
stack 是用來存放簡單型別的變數與物件的參考,這裡的資料大小是必須確定的,存取的速度也比較快。
heap 是用來儲存物件真實資料的地方,由於我們通常不知道物件裡頭會有哪些東西,所以是動態的,速度也就比較慢一點。
回顧上方兩組範例程式,分別宣告了這些變數:
var a = 1, b = 2;
var obj = {
a: 1,
b: 2
};
這些在記憶體中會長什麼樣子呢?我們來畫個示意圖:
當我們把obj傳進swap時,又會發生什麼事呢?
記憶體中會多一塊變數 p,但 p 存的也是 obj 所存的那個位址,當要修改 p 時,就是循著這個位址去找到真正儲存資料的地方,對 p 的修改其實就是對 obj 的修改,都是修改真正儲存資料的地方,所以就會互為影響。
這部分即使沒有特別討論,但大家平常應該很常在使用,會想要特別提出來說,是因為牽涉到 closure,這個就靜待下回分解啦。